/// ****************************************************************************
/// Author:	        Robin Zhu
/// Create Date:	2011-11-1
/// Purpose: 		数据库操作执行类文件
/// ****************************************************************************
/// Modify By		Date			Remark
/// Robin           2012-3-23       修复当仅返回一个字符串类型的字段时，类型判断上的Bug
/// Robin           2012-3-26       由于没有非托管资源，所以去掉析构函数
/// Robin           2012-4-3        增加一个判断事务是否启用的方法，并在启用和关闭事务的方法中加锁
/// Robin           2012-6-14       修改创建类实例的方式，支持匿名类
/// Robin           2012-8-15       改为使用yield return返回记录集
/// Robin           2013-4-30       使用Delegate设置生成的对象的属性
/// Robin           2014-7-9        使用Trace.WriteLine代替Debug.Print，以便在Release版中跟踪生成的SQL语句
/// ****************************************************************************

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.Linq.Expressions;
using RB.DataAccess.Linq;
using System.Collections;
using System.Reflection;

namespace RB.DataAccess
{
    /// <summary>
    /// 执行数据库调用的接口，不同的数据库应有不同的实现
    /// </summary>
    public abstract class Executor : IDisposable
    {
        /// <summary>
        /// 用于锁定connection的操作
        /// </summary>
        private object _locker = new object();

        #region 数据库连接计数

        private static int _ConnectionCount = 0;
        private static int _ConnectionPeakCount = 0;
        private static int _ConnectionTotalCount = 0;

        /// <summary>
        /// 当前打开的数据库连接数
        /// </summary>
        public static int ConnectionCount
        {
            get
            {
                return _ConnectionCount;
            }
            private set
            {
                if (_ConnectionCount < value)
                {
                    int diff = (value - _ConnectionCount);
                    if (int.MaxValue - diff > _ConnectionTotalCount)
                        _ConnectionTotalCount += diff;
                }
                _ConnectionCount = value;
                if (_ConnectionCount > _ConnectionPeakCount)
                {
                    _ConnectionPeakCount = _ConnectionCount;
                }
            }
        }

        /// <summary>
        /// 到目前为止同时打开数据库连接的最大值
        /// </summary>
        public static int ConnectionPeakCount
        {
            get
            {
                return _ConnectionPeakCount;
            }
        }

        /// <summary>
        /// 到目前为止打开数据库的次数
        /// </summary>
        public static int ConnectionTotalCount
        {
            get
            {
                return _ConnectionTotalCount;
            }
        }

        #endregion 数据库连接计数

        #region 开关数据库连接

        internal void CloseConnection()
        {
            lock (_locker)
            {
                if (_transaction == null && _connection != null)
                {
                    _connection.Close();
                    //ConnectionCount--;
                    Debug.Print(string.Format("-Connection Count: {0}", (ConnectionCount > 0) ? --ConnectionCount : 0));
                }
            }
        }

        #endregion

        private DataContext _dataContext = null;
        private DbConnection _connection = null;
        private DbTransaction _transaction = null;

        protected Executor(DataContext dataContext)
        {
            _dataContext = dataContext;
        }

        // Do not call Close or Dispose on a Connection, a DataReader, 
        // or any other managed object in the Finalize method of your class. 
        // In a finalizer, you should only release unmanaged resources that your class owns directly. 
        // If your class does not own any unmanaged resources, 
        // do not include a Finalize method in your class definition. 
        // For more information, see Garbage Collection.
        //~Executor()
        //{
        //    Dispose(false);
        //}

        public DataContext DataContext
        {
            get
            {
                return _dataContext;
            }
        }

        protected DbConnection Connection
        {
            get
            {
                lock (_locker)
                {
                    if (_connection == null)
                    {
                        _connection = _dataContext.NewConnection();
                        _connection.ConnectionString = _dataContext.ConnectionString;
                    }
                    if (_connection.State == ConnectionState.Closed)
                    {
                        // 如果连接被关闭，则重新打开
                        _connection.Open();

                        //ConnectionCount++;
                        Debug.Print(string.Format("+Connection Count: {0}", ++ConnectionCount));
                    }
                    return _connection;
                }
            }
        }

        /// <summary>
        /// 判断当前连接是否已启用事务
        /// </summary>
        /// <returns></returns>
        public bool IsTransactionBegin()
        {
            lock (_locker)
            {
                return _transaction != null;
            }
        }

        /// <summary>
        /// 启动一个事务
        /// </summary>
        public void BeginTransaction()
        {
            lock (_locker)
            {
                if (_transaction == null)
                    _transaction = Connection.BeginTransaction();
            }
        }

        public void BeginTransaction(IsolationLevel iso)
        {
            lock (_locker)
            {
                if (_transaction == null)
                    _transaction = Connection.BeginTransaction(iso);
            }
        }

        /// <summary>
        /// 提交当前事务
        /// </summary>
        public void CommitTransaction()
        {
            lock (_locker)
            {
                if (_transaction != null)
                {
                    _transaction.Commit();
                    _transaction.Dispose();
                    _transaction = null;
                }
                CloseConnection();
            }
        }

        /// <summary>
        /// 回滚当前事务
        /// </summary>
        public void RollbackTransaction()
        {
            lock (_locker)
            {
                if (_transaction != null)
                {
                    _transaction.Rollback();
                    _transaction.Dispose();
                    _transaction = null;
                }
                CloseConnection();
            }
        }

        public IDataReader ExecuteReader(string sql)
        {
            Trace.WriteLine("DataAccess.ExecuteReader: " + sql);
            DbCommand cmd = CreateCommand(sql);

            return cmd.ExecuteReader();
        }

        /// <summary>
        /// 执行一条非查询的SQL语句
        /// </summary>
        /// <param name="sql">非查询的SQL语句</param>
        /// <returns>受影响行数</returns>
        public virtual int Execute(string sql)
        {
            if (string.IsNullOrWhiteSpace(sql))
                throw new ArgumentException("SQL statement to be executed is not provided.");
            Trace.WriteLine("DataAccess.Execute: " + sql);
            DbCommand cmd = CreateCommand(sql);

            try
            {
                int n = cmd.ExecuteNonQuery();

                return n;
            }
            catch
            {
                throw;
            }
            finally
            {
                CloseConnection();
            }
        }

        /// <summary>
        /// 执行一条查询语句，并返回第一行记录的第一个字段值
        /// </summary>
        /// <param name="sql">查询语句</param>
        /// <returns>第一行记录的第一个字段值</returns>
        public virtual object ExecuteScalar(string sql)
        {
            if (String.IsNullOrEmpty(sql))
                throw new ArgumentException("SQL statement to be executed is not provided.");
            Trace.WriteLine("DataAccess.ExecuteScalar: " + sql);
            DbCommand cmd = CreateCommand(sql);

            object result = null;
            try
            {
                result = cmd.ExecuteScalar();
            }
            catch
            {
                throw;
            }
            finally
            {
                CloseConnection();
            }
            return result;
        }

        protected DbCommand CreateCommand(string sql)
        {
            DbCommand cmd = _dataContext.NewCommand();
            cmd.Connection = Connection;
            cmd.Transaction = _transaction;
            cmd.CommandType = CommandType.Text;
            cmd.CommandText = sql;

            return cmd;
        }

        public virtual int Insert<T>(T entity)
        {
            return Insert(entity, null);
        }

        public virtual int Insert<T>(T entity,  string mappingTo)
        {
            return Insert<T, T>(p => entity, mappingTo);
        }

        public virtual int Insert<T, TFields>(Expression<Func<T, TFields>> fields)
        {
            return Insert(fields, null);
        }

        public virtual int Insert<T, TFields>(Expression<Func<T, TFields>> fields, string mappingTo)
        {
            var sqlBuilder = new SqlBuilder(_dataContext.NewSqlEncoder());
            string sql = sqlBuilder.BuildInsertCommand(fields, mappingTo);
            return Execute(sql);
        }

        public virtual int Insert<TFields>(IQueryable<TFields> query, string mappingTo)
        {
            var sqlBuilder = new SqlBuilder(_dataContext.NewSqlEncoder());
            string sql = sqlBuilder.BuildInsertCommand<TFields>(query, mappingTo);
            return Execute(sql);
        }

        public int Delete<T>(Expression<Func<T, bool>> predict)
        {
            return Delete<T>(predict, null);
        }

        public virtual int Delete<T>(Expression<Func<T, bool>> predict, string mappingTo)
        {
            var sqlBuilder = new SqlBuilder(_dataContext.NewSqlEncoder());
            string sql = sqlBuilder.BuildDeleteCommand(predict);
            return Execute(sql);
        }

        public virtual int Delete<T>(IQueryable<T> query)
        {
            var sqlBuilder = new SqlBuilder(_dataContext.NewSqlEncoder());
            string sql = sqlBuilder.BuildDeleteCommand(query);
            return Execute(sql);
        }

        public virtual int Update<T>(T entity, Expression<Func<T, bool>> predict)
        {
            return Update(entity, predict, null);
        }

        public virtual int Update<T>(T entity, Expression<Func<T, bool>> predict, string mappingTo)
        {

            return Update<T, T>(p => entity, predict, mappingTo);
        }

        public virtual int Update<T, TFields>(Expression<Func<T, TFields>> fields, Expression<Func<T, bool>> predict)
        {
            return Update(fields, predict, null);
        }

        public virtual int Update<T, TFields>(Expression<Func<T, TFields>> fields, Expression<Func<T, bool>> predict, string mappingTo)
        {
            var sqlBuilder = new SqlBuilder(_dataContext.NewSqlEncoder());
            string sql = sqlBuilder.BuildUpdateCommand(fields, predict, mappingTo);
            return Execute(sql);
        }

        public virtual int Update<T, TFields>(Expression<Func<T, TFields>> fields, IQueryable<T> query)
        {
            var sqlBuilder = new SqlBuilder(_dataContext.NewSqlEncoder());
            string sql = sqlBuilder.BuildUpdateCommand(fields, query);
            return Execute(sql);
        }

        public virtual SqlQueryable<T> Query<T>()
        {
            return Query<T>(null);
        }

        public virtual SqlQueryable<T> Query<T>(string mappingTo)
        {
            SqlQueryableProvider provider = new SqlQueryableProvider(this);
            return new SqlQueryable<T>(provider, mappingTo);
        }

        public virtual T ExecuteScalar<T>(Expression expression)
        {
            var sqlBuilder = new SqlBuilder(_dataContext.NewSqlEncoder());
            string sql = sqlBuilder.BuildSelectCommand(expression);
            
            object result = ExecuteScalar(sql);
            return TypeSystem.ConvertField<T>(result);
        }

        internal virtual IEnumerable<T> ExecuteQuery<T>(Expression expression)
        {
            var sqlBuilder = new SqlBuilder(_dataContext.NewSqlEncoder());
            string sql = sqlBuilder.BuildSelectCommand(expression);

            return ExecuteQuery<T>(sql);
        }

        /// <summary>
        /// 执行一个SQL查询，并将查询结果放入一个强类型的列表中
        /// </summary>
        /// <typeparam name="T">返回的记录对应的数据类型</typeparam>
        /// <param name="querySql">SQL查询</param>
        /// <returns>记录列表</returns>
        public virtual IEnumerable<T> ExecuteQuery<T>(string querySql)
        {
            Type t = TypeSystem.GetElementType(typeof(T));
            bool isComplex = (t.IsClass || !t.IsPrimitive && !t.IsEnum && !TypeSystem.IsNullableType(t)) && !(typeof(T) == typeof(string));

            //List<T> result = new List<T>();

            using (IDataReader reader = ExecuteReader(querySql))
            {
                //try
                //{
                    T element = default(T);
                    // 对字段对应的类属性列表进行预处理
                    
                    PropertyInfo[] properties = null;
                    object[] propertyValues = null;
                    IMemberSetter[] setters = null;

                    if (isComplex)
                    {
                        GetPropertyMappingArray<T>(reader, out properties, out setters);
                        propertyValues = new object[reader.FieldCount];
                    }

                    while (reader.Read())
                    {
                        if (isComplex)
                        {
                            reader.GetValues(propertyValues);
                            element = TypeMapper<T>.Construct(propertyValues, properties, setters);
                        }
                        else
                        {
                            element = TypeSystem.ConvertField<T>(reader[0]);
                        }

                        //result.Add(element);
                        yield return element;
                    }
                    //reader.Close();
                //}
                //catch
                //{
                //    reader.Close();
                //    throw;
                //}
                //finally
                //{
                    // 如果不是在事务中，这应关闭连接，以免连接打开过多
                    
                //}
            }
            CloseConnection();
            //return result;
        }

        /// <summary>
        /// 取得类型中与字段表对应的属性信息数组
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="reader"></param>
        /// <returns></returns>
        private void GetPropertyMappingArray<T>(IDataReader reader, out PropertyInfo[] properties, out IMemberSetter[] setters)
        {
            var map = TypeMapper<T>.QueryFieldMap;

            if (map.Count == reader.FieldCount)
            {
                properties = map.Values.Select(p => p.Item1).ToArray();
                setters = map.Values.Select(p => p.Item2).ToArray();
            }
            else
            {
                properties = new PropertyInfo[reader.FieldCount];
                setters = new IMemberSetter[reader.FieldCount];
                for (int i = 0; i < reader.FieldCount; i++)
                {
                    string fieldName = reader.GetName(i);
                    if (map.ContainsKey(fieldName))
                    {
                        var values = map[fieldName];
                        properties[i] = values.Item1;
                        setters[i] = values.Item2;
                    }
                }
            }
        }

        //public abstract TableSchema GetTableSchema(string tableName);

        /// <summary>
        /// 实现IDisposable
        /// </summary>
        #region IDisposable Members

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            lock (_locker)
            {
                if (disposing)
                {
                    // free native resources if there are any.
                    if (_transaction != null)
                    {
                        CommitTransaction();
                        _transaction = null;
                    }
                    CloseConnection();

                    if (_connection != null)
                    {
                        _connection.Dispose();
                        _connection = null;
                    }
                }
            }
        }

        #endregion IDisposable Members

    }
}
